import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class Pandemic {

	private Tablero tablero;
	/**
	 * @uml.property name="jugador"
	 * @uml.associationEnd multiplicity="(2 4)" ordering="true" inverse="pandemic:Jugador"
	 */
	private List<Jugador> jugadores;
	/**
	 * @uml.property  name="infeccion"
	 * @uml.associationEnd  inverse="pandemic:Infeccion"
	 */
	private Infeccion infeccion;
    private Mazo<CartaInfeccion> mazoInfeccion;
	private Mazo<CartaInfeccion> mazoInfeccionDescartado;
	private Mazo<CartaJugador> mazoJugador;
	private Mazo<CartaJugador> mazoJugadorDescartado;
	private Iterator<Jugador> jugadoresIt;

	private boolean noche = false;
	
	public void addJugador(String nombreJugador,int rol) {
		Jugador jugador;
		switch(rol){
			case 1: jugador = new JugadorInvestigador(nombreJugador,tablero.ciudadInicial,Roles.Investigador);
					jugadores.add(jugador);
					break;
			case 2: jugador = new JugadorCientifico(nombreJugador,tablero.ciudadInicial,Roles.Cientifico);
					jugadores.add(jugador);
					break;
			case 3: jugador = new JugadorTransportista(nombreJugador,tablero.ciudadInicial,Roles.Transportista);
					jugadores.add(jugador);
					break;
			case 4: jugador = new JugadorMedico(nombreJugador,tablero.ciudadInicial,Roles.Medico);
					jugadores.add(jugador);
					break;
			case 5: jugador = new JugadorExpertoOperaciones(nombreJugador,tablero.ciudadInicial,Roles.ExpertoOperaciones);
					jugadores.add(jugador);
					break;
		}
	}

	public Pandemic() {
		iniciaTablero();
		iniciaJugadores();
		iniciaInfeccion();
		iniciaMazos();
	}

	public void iniciaTablero(){
		tablero = new Tablero(Ciudad.ATLANTA);
	}

	private void iniciaJugadores(){
		jugadores = new LinkedList<Jugador>();
	}

	private void iniciaInfeccion(){
		infeccion = new Infeccion();
	}

	private void iniciaMazos(){
		MazoFactory mf1 = new MazoFactory();
	    mazoInfeccion = mf1.creaCartasInfeccion();
	    mazoInfeccionDescartado= new Mazo<CartaInfeccion>();
		MazoFactory mf2 = new MazoFactory();
		mazoJugador= mf2.creaCartasJugador();
		mazoJugadorDescartado= new Mazo<CartaJugador>();
    }

	public static int leer(int rango){
	    BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
        boolean esEntero = false;
        int opcion=0;
        while(!esEntero){
       	 	try {
       	 			esEntero = true;
       	 			opcion = Integer.parseInt(buf.readLine());
               } catch (IOException e) {
               		System.out.println(e.toString());
               		esEntero = false;
               } catch (NumberFormatException e){
                       esEntero = false;
               }
             if (opcion<0 || opcion>(rango-1)){
             	System.out.println("Elija una opcion de 0 a "+(rango-1));
               	esEntero = false;
             }
        }
        return opcion;
	}

	public void jugar(){
		preparacion();
		Jugador jugadorActual;
		int fin=0;
		while(fin==0){
			jugadoresIt = jugadores.iterator();
			while(jugadoresIt.hasNext()){
				jugadorActual = siguienteJugador();
				System.out.println("////////////////////////////////////////////////");
				System.out.println("TURNO DEL JUGADOR: " + jugadorActual.getNombre()+" "+jugadorActual.getRol());
				//1.-REALIZA CUATRO ACCIONES, MENOS O PASA
				turnoJugador(jugadorActual);
				//2.-roba dos cartas
				robarCartas(jugadorActual,mazoJugadorDescartado,2);
				//3.-turno del jugador infeccion
				if(!noche){
					turnoInfeccion();
				}else{
					noche=false;
					System.out.println("Gracias a la carta NOCHE TRANQUILA no se realiza el turno de infeccion");
				}
			}
		}
	}

	private void preparacion(){
		mazoJugador.baraja();
		mazoInfeccion.baraja();
		reparteCartasJugador();
		seleccionaDificultad();
		reparteCartasInfeccion(3,3,false);
		reparteCartasInfeccion(3,2,false);
		reparteCartasInfeccion(3,1,false);
	}

	private void reparteCartasJugador(){
		int nc=4;
		if(jugadores.size()==3){
			nc=3;
		}
		if(jugadores.size()==4){
			nc=2;
		}
        for(Jugador j:jugadores){
	           robarCartas(j,mazoJugadorDescartado,nc);
	    }
	}

	private void seleccionaDificultad(){
		System.out.println("Selecciona dificultad: ");
		System.out.println("0) Introductoria");
		System.out.println("1) Normal");
		System.out.println("2) Heroica");
		int opcion=leer(3);
		//repartir cartas de epidemia entre las cartas de jugador restantes
		//despues de que los jugadores hayan robado por primera vez
		//como max entre todos roban 9 cartas (juego de 3 jugadores)= 53-9 = 44
		int indice;
		if(opcion==0){
			indice=new Double(Math.random() * 11).intValue();
			mazoJugador.getMazo().subList(0,11).add(indice,new CartaEpidemia(0));
			indice=new Double(Math.random() * 11).intValue();
			mazoJugador.getMazo().subList(11,22).add(indice,new CartaEpidemia(1));
			indice=new Double(Math.random() * 11).intValue();
			mazoJugador.getMazo().subList(22,33).add(indice,new CartaEpidemia(2));
			indice=new Double(Math.random() * 11).intValue();
			mazoJugador.getMazo().subList(33,45).add(indice,new CartaEpidemia(3));
		}
		if(opcion==1){
			indice=new Double(Math.random() * 8).intValue();
			mazoJugador.getMazo().subList(0,8).add(indice,new CartaEpidemia(0));
			indice=new Double(Math.random() * 8).intValue();
			mazoJugador.getMazo().subList(8,16).add(indice,new CartaEpidemia(1));
			indice=new Double(Math.random() * 8).intValue();
			mazoJugador.getMazo().subList(16,25).add(indice,new CartaEpidemia(2));
			indice=new Double(Math.random() * 8).intValue();
			mazoJugador.getMazo().subList(25,33).add(indice,new CartaEpidemia(3));
			indice=new Double(Math.random() * 8).intValue();
			mazoJugador.getMazo().subList(33,45).add(indice,new CartaEpidemia(4));
		}
		if(opcion==2){
			indice=new Double(Math.random() * 7).intValue();
			mazoJugador.getMazo().subList(0,7).add(indice,new CartaEpidemia(0));
			indice=new Double(Math.random() * 7).intValue();
			mazoJugador.getMazo().subList(7,14).add(indice,new CartaEpidemia(1));
			indice=new Double(Math.random() * 7).intValue();
			mazoJugador.getMazo().subList(14,21).add(indice,new CartaEpidemia(2));
			indice=new Double(Math.random() * 7).intValue();
			mazoJugador.getMazo().subList(21,28).add(indice,new CartaEpidemia(3));
			indice=new Double(Math.random() * 7).intValue();
			mazoJugador.getMazo().subList(28,35).add(indice,new CartaEpidemia(4));
			indice=new Double(Math.random() * 7).intValue();
			mazoJugador.getMazo().subList(35,44).add(indice,new CartaEpidemia(5));
		}
	}

	void reparteCartasInfeccion(int numerocartas,int numerocubos,boolean epidemia){
		//inicia las casillas con brotes a ninguna
		infeccion.inicializaCasillasbrotadas();
		for(int i=0;i<numerocartas;i++){
			if(!mazoInfeccion.getMazo().isEmpty()){
				CartaInfeccion cI;
				if(!epidemia) cI=mazoInfeccion.getCartaDeArriba();
				else cI=mazoInfeccion.getCartaDeAbajo();
				mazoInfeccionDescartado.robaMazo(cI,mazoInfeccion);
				infeccion.infectarCiudad(cI.getCiudad(),cI.getColor(),numerocubos);
			}
			else{
				System.out.println("********************************************");
				System.out.println("Se han acabado las cartas de Infeccion");
				System.out.println("Barajo la pila de descarte de Infeccion y vuelvo a ponerla en juego");
				mazoInfeccionDescartado.baraja();
				mazoInfeccion.getMazo().addAll(mazoInfeccionDescartado.getMazo());
				mazoInfeccionDescartado.getMazo().removeAll(mazoInfeccion.getMazo());
			}
		//si ha habido brotes los borra del historial de casillas brotadas para el siguiente turno de infeccion
		infeccion.borraCasillasbrotadas();
		}
	}

	private Jugador siguienteJugador() {
		if (!jugadoresIt.hasNext()) {
			jugadoresIt = jugadores.iterator();
			while (jugadoresIt.hasNext())
				System.out.println(jugadoresIt.next());
			jugadoresIt = jugadores.iterator();
		}
		return jugadoresIt.next();
	}

	private void turnoJugador(Jugador jugadorActual){
		int opcion,opcion2;
		int acciones=4;
			while(acciones!=0){
				System.out.println("ACCIONES RESTANTES: "+acciones+" �Que quieres hacer "+ jugadorActual.getNombre()+" "+jugadorActual.getRol()+"?");
				opcion=imprimirMenuPrincipal();
				switch(opcion){
					case 0:	tablero.imprimirCiudadesInfectadas();
							break;
					case 1:	JugadorTransportista jugadorAuxiliar2=null;
							if(jugadorActual instanceof JugadorTransportista){
								jugadorAuxiliar2=(JugadorTransportista) jugadorActual;
								Jugador jugadorAuxiliar=((JugadorTransportista) jugadorActual).accionEspecial(jugadores);
								jugadorActual=jugadorAuxiliar;
							}
							opcion2=imprimirMenuAccionBasica();
							switch(opcion2){
								case 0:	//conducir
										jugadorActual.conducir();
										acciones--;
										break;
								case 1:	//vuelo directo
										if(jugadorActual.vueloDirecto(mazoJugadorDescartado)) acciones--;
										break;
								case 2:	//vuelo charter
										if(jugadorActual.vueloCharter(mazoJugadorDescartado)) acciones--;
										break;
								case 3:	//vuelo privado
										if(jugadorActual.vueloPrivado()) acciones--;
										break;
								case 4:	//volver al menu ppal
										break;
							}
							if(jugadorAuxiliar2 instanceof JugadorTransportista) jugadorActual=jugadorAuxiliar2;
							break;
					case 2: opcion2=imprimirMenuEspecial();
							switch(opcion2){
							case 0:	//construir centro investigacion
									if(jugadorActual instanceof JugadorExpertoOperaciones){
										if(((JugadorExpertoOperaciones) jugadorActual).accionEspecial()) acciones--;
									}
									else if (jugadorActual.construirCentroInvestigacion(mazoJugadorDescartado))	acciones--;
									break;
							case 1:	//descubrir una cura
									if(jugadorActual instanceof JugadorCientifico){
										if(jugadorActual.descubrirCura(mazoJugadorDescartado,4))
											acciones--;
									}else{
										if(jugadorActual.descubrirCura(mazoJugadorDescartado,5))
											acciones--;
										}
									break;
							case 2:	//tratar una enfermedad
									if(jugadorActual instanceof JugadorMedico){
										jugadorActual.tratarEnfermedad(true);
									}
									else if(jugadorActual.tratarEnfermedad(false)) acciones--;
									break;
							case 3:	//compartir conocimientos
									if(jugadorActual instanceof JugadorInvestigador){
										if(jugadorActual.compartirConocimientos(jugadores,mazoJugadorDescartado,true))
											acciones--;
									}
									else{
										if(jugadorActual.compartirConocimientos(jugadores,mazoJugadorDescartado,false))
											acciones--;
									}
									break;
							case 4:	//volver al menu ppal
									break;
							}
							break;
					case 3: jugadorActual.imprimirJugada();
							break;
					case 4: tablero.imprimirTodasLasCiudades();
							break;
					case 5: imprimirPosicionJugadores();
							break;
					case 6: Tablero.imprimirCentrosInvestigacion();
							break;
					case 7: //utilizar carta de eventos especiales
							List<CartaJugador> lee = new LinkedList<CartaJugador>();
							for(int i=0;i<jugadores.size();i++){
								Mazo<CartaJugador> mano = jugadores.get(i).getJugada();
								for(int j=0;j<mano.MazoSize();j++){
									if(mano.getCartaPorIndice(j) instanceof CartaEventoEspecial){
										lee.add(mano.getCartaPorIndice(j));
									}
								}
							}
							if(lee.isEmpty()){
								System.out.println("Ningun jugador dispone de cartas de eventos especiales");
								break;
							}
							String evento = imprimirEventosDisponibles(lee);
							if(evento.equals("PREVISION")){
								utilizarPrevision();
							}
							else if(evento.equals("SUBVENCION_GOBIERNO")){
								utilizarSubvencion();
							}
							else if(evento.equals("NOCHE_TRANQUILA")){
								utilizarNoche();
							}
							else if(evento.equals("PUENTE_AEREO")){
								utilizarPuente();
							}
							else{
								utilizarPoblacion();
							}
							descartarEventoEspecial(evento);
							System.out.println("*******************************************************");
							break;
					case 8: acciones=0;
							break;
				}
			}
	}

	private int imprimirMenuPrincipal(){
		System.out.println("");
		System.out.println("MENU PRINCIPAL");
		System.out.println("0) Ver las ciudades infectadas");
		System.out.println("1) Realizar Accion Basica");
		System.out.println("2) Realizar Accion Especial");
		System.out.println("3) Ver tus cartas");
		System.out.println("4) Ver el mapa completo");
		System.out.println("5) �Donde estan mis compa�eros?");
		System.out.println("6) �Donde hay centros de Investigacion?");
		System.out.println("7) Algún jugador desea utilizar carta de Eventos Especiales");
		System.out.println("8) Pasar");
		return leer(9);
	}

	private int imprimirMenuAccionBasica() {
		System.out.println("");
		System.out.println("MENU DE ACCIONES BASICAS");
		System.out.println("0) Viajar a ciudad adyacente");
		System.out.println("1) Vuelo directo");
		System.out.println("2) Vuelo Charter");
		System.out.println("3) Vuelo privado");
		System.out.println("4) Volver al menu principal");
		return leer(5);
	}

	private int imprimirMenuEspecial() {
		System.out.println("");
		System.out.println("MENU DE ACCIONES ESPECIALES");
		System.out.println("0) Construir un centro de investigacion");
		System.out.println("1) Descubrir una cura");
		System.out.println("2) Tratar una enfermedad");
		System.out.println("3) Compartir conocimientos");
		System.out.println("4) Volver al menu principal");
		return leer(5);
	}

	private void imprimirPosicionJugadores(){
		System.out.println("*****************************************");
		for(int i=0;i<jugadores.size();i++){
			Jugador j=jugadores.get(i);
			System.out.println("JUGADOR "+j.getNombre()+" "+j.getRol()+" ESTA EN "+j.getPosicionActual().getCiudad());
		}
		System.out.println("*****************************************");
	}

	private String imprimirEventosDisponibles(List<CartaJugador> lcee){
		System.out.println("");
		System.out.println("SELECCIONE CARTA DE EVENTOS ESPECIALES:");
		for(int i=0;i<lcee.size();i++){
			System.out.println(i+") "+((CartaEventoEspecial) lcee.get(i)).getEventoEspecial());
		}
		int opcionEvento = leer(lcee.size());
		EventoEspecial ee = ((CartaEventoEspecial) lcee.get(opcionEvento)).getEventoEspecial();
		return ee.name();
	}

	private void robarCartas(Jugador jugadorActual,Mazo<CartaJugador> mazojugadordescartado,int limite) {
		if(mazoJugador.MazoSize()==0){
			finJugar(2,null);
		}
		System.out.println("JUGADOR "+jugadorActual.getNombre()+" "+jugadorActual.getRol()+ " ROBA "+limite+" CARTAS:");

		for(int i=0;i<limite;i++){
			CartaJugador nuevacarta = this.mazoJugador.getCartaDeArriba();
			//si la carta es de epidemia se ejecuta y no se guarda en la jugada del jugador actual
			if (nuevacarta instanceof CartaEpidemia){
				((CartaEpidemia) nuevacarta).epidemia(this,mazoInfeccion,mazoInfeccionDescartado);
			}
			//si tiene <7 la guarda en la jugada del jugador actual
			else if(jugadorActual.getJugada().MazoSize()<7){
				jugadorActual.getJugada().robaMazo(nuevacarta, mazoJugador);
			}
			else{//si tiene 7 tiene que descartar una
				List<Integer> indices=new LinkedList<Integer>();
				if(jugadorActual.getJugada().MazoSize()!=0)
				{
					System.out.println("Jugador "+jugadorActual.getNombre()+" "+jugadorActual.getRol()+" tienes que descartar una carta:");
					int j=0;
					for(int k=0;k<jugadorActual.getJugada().MazoSize();k++){
						Carta carta = jugadorActual.getJugada().getCartaPorIndice(k);
						if(carta instanceof CartaCiudad) System.out.println(j+") Ciudad: "+carta.getCiudad()+" Color: "+carta.getColor());
						if(carta instanceof CartaEventoEspecial) System.out.println(j+") Evento Especial: "+((CartaEventoEspecial) carta).getEventoEspecial());
			          	indices.add(k);
						j++;
					}
					int opcion=Pandemic.leer(indices.size());
					Carta cartadescartada=jugadorActual.getJugada().getCartaPorIndice(indices.get(opcion));
					if(cartadescartada instanceof CartaCiudad) System.out.println(j+") Ciudad: "+cartadescartada.getCiudad()+" Color: "+cartadescartada.getColor());
					if(cartadescartada instanceof CartaEventoEspecial) System.out.println(j+") Evento Especial: "+((CartaEventoEspecial) cartadescartada).getEventoEspecial());
		          	mazojugadordescartado.robaMazo(jugadorActual.getJugada().getCartaPorIndice(indices.get(opcion)),jugadorActual.getJugada());
					jugadorActual.getJugada().robaMazo(nuevacarta, mazoJugador);
				}
			}
		}
		jugadorActual.imprimirJugada();
	}

	private void turnoInfeccion(){
		System.out.println("////////////////////////////////////////////////");
		System.out.println("TURNO DE LA INFECCION");
		System.out.println("Roba "+Tablero.ritmoInfeccion.getMultiplicador()+" cartas:");
		reparteCartasInfeccion(Tablero.ritmoInfeccion.getMultiplicador(),1,false);
	}

	public static void finJugar(int opcion, Color color){
		System.out.println("************FIN DEL JUEGO*****************");
		if(opcion==0){
			System.out.println("DERROTA EL CONTADOR DE BROTES HA LLEGADO A 8");
		}
		if(opcion==1){
			 System.out.println("DERROTA NO HAY MAS CUBOS DE COLOR "+color);
		}

		if(opcion==2){
			System.out.println("DERROTA SE HAN ACABADO LAS CARTAS DE JUGADOR");
		}
		if(opcion==3){
			System.out.println("VICTORIA!!! SE HAN DESCUBIERTO TODAS LAS CURAS");
		}

		System.out.println("�QUIERES VOLVER A JUGAR?");
		System.out.println("0) SI, ME GUSTARIA DERROTAR A CHUCK NORRIS ALGUN DIA");
		System.out.println("1) NO, VOY A LA FARMACIA A POR EL TAMIFLU FORMATO SUPOSITORIO");
		int opcionfin=Pandemic.leer(2);
		if(opcionfin==0) Main.main(null);
		else {
			System.out.println("BYE!");
			System.exit(-1);
		}
	}

	public void incrementaIntensidadInfeccion(){
		mazoInfeccionDescartado.baraja();
		for(int i=0;i<mazoInfeccionDescartado.getMazo().size();i++){
			CartaInfeccion carta = ((LinkedList<CartaInfeccion>) mazoInfeccionDescartado.getMazo()).removeLast();
			((LinkedList<CartaInfeccion>) mazoInfeccion.getMazo()).addFirst(carta);
		}
	}

	public void descartarEventoEspecial(String ee){
		for(int i=0;i<jugadores.size();i++){
			Mazo<CartaJugador> mano = jugadores.get(i).getJugada();
			for(int j=0;j<mano.MazoSize();j++){
				CartaJugador cj = mano.getCartaPorIndice(j);
				if(cj instanceof CartaEventoEspecial && ((CartaEventoEspecial)cj).getEventoEspecial().name().equals(ee))
					mano.removeCarta(cj);
			}
		}
	}


	//Prevision: examina las 6 cartas de la parte alta de la pila d infeccion, ordenalas como quieras y colocala	
	public void utilizarPrevision(){
		System.out.println("");
		System.out.println("Ordena como quieras las 6 cartas de la parte alta del mazo de infecci�n");
		System.out.println("Las cartas de infecci�n son:");
		List<CartaInfeccion> lci = new LinkedList<CartaInfeccion>();
		for(int i=0;i<6;i++){
			CartaInfeccion ci = mazoInfeccion.getCartaPorIndice(i);
			lci.add(ci);
			System.out.println(i+") "+ci.getCiudad().name());
		}
		for(int j=0;j<6;j++){
			int opcion = leer(6);
			CartaInfeccion ci = lci.get(opcion);
			mazoInfeccion.setCartaPorIndice(j,ci);
		}	
	}

	//Subvencion Gobierno: a�ade 1 centro a cualquier ciudad
	public void utilizarSubvencion(){
		System.out.println("");
		System.out.println("Elige una ciudad para construir un centro de investigaci�n:");
		Tablero.imprimirCiudades();
		int indice=Pandemic.leer(Tablero.casillasCiudades.size());
		CasillaCiudad ciudadElegida = Tablero.casillasCiudades.get(indice);
		if(!CasillaCiudad.tieneCentro(ciudadElegida)){
			Tablero.centrosInvestigacion.add(ciudadElegida);
		}
		else{
			System.out.println("Esta ciudad ya tiene un centro de investigaci�n");
			utilizarSubvencion();
		}
	}

	//Noche Tranquila: el jugador actual puede saltarse el turno d infeccion
	public void utilizarNoche(){
		noche=true;
	}

	//Puente Aereo: mueve un peon a cualquier ciudad
	public void utilizarPuente(){
		System.out.println("");
		System.out.println("Elige un jugador para mover a otra ciudad:");
		for(int i=0;i<jugadores.size();i++){
			System.out.println(i+") "+jugadores.get(i).getNombre());
		}
		int opcion1 = leer(jugadores.size());
		Jugador jug = jugadores.get(opcion1);	
		System.out.println("");
		System.out.println("Elige una ciudad a la que moverlo:");
		Tablero.imprimirCiudades();
		int opcion2 = leer(Tablero.casillasCiudades.size());
		jug.setPosicionActual(Tablero.casillasCiudades.get(opcion2));
	}

	//Poblacion Resistente: coge una carta d descartes d infeccion y retirala del juego
	public void utilizarPoblacion(){
		mazoInfeccionDescartado.removeCarta(mazoInfeccionDescartado.getCartaDeArriba());
	}

}
